/*
 * (C) Copyright 2024
 * Derived from the USB keyboard driver code.
 *
 * SPDX-License-Identifier: GPL-2.0+
 */

#include <common.h>
#include <console.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <memalign.h>
#include <stdio_dev.h>
#include <asm/byteorder.h>

#include <usb.h>

/* Mouse data packet size */
#define USB_MOUSE_PACKET_SIZE 4
#define REPEAT_RATE	40	
/* Device name */
#define DEVNAME "usbmouse"

struct usb_mouse_pdata {
    unsigned long intpipe;
    int intpktsize;
    int intinterval;
    unsigned long last_report;
    struct int_queue *intq;

    uint8_t *new;
    uint8_t old[USB_MOUSE_PACKET_SIZE];
};
void usb_mouse_get(){
    struct stdio_dev *dev;
    struct usb_device *usb_mouse_dev;
    dev = stdio_get_by_name(DEVNAME);
    usb_mouse_dev=(struct usb_device *)dev->priv;
    struct usb_mouse_pdata *data1= usb_mouse_dev->privptr;
    usb_mouse_poll_for_event(usb_mouse_dev);
    // int dx, dy, button;
    // button = data1->new[0];
    // dx = (int8_t)data1->new[1];
    // dy = (int8_t)data1->new[2];
    // if (button & 0x01)
    //     printf("Left button pressed\n");
    // if (button & 0x02)
    //     printf("Right button pressed\n");
    // if (button & 0x04)
    //     printf("Middle button pressed\n");
    // if (dx || dy)
    //     printf("Mouse moved: dx=%d, dy=%d\n", dx, dy);
}

/* Interrupt service routine */
static int usb_mouse_irq_worker(struct usb_device *dev)
{
    struct usb_mouse_pdata *data = dev->privptr;
    int dx, dy, button;
    printf("usb_mouse_irq_worker\n");
    printf("%d__%d__%d\n",data->new[0],data->new[1],data->new[2]);
    button = data->new[0];
    dx = (int8_t)data->new[1];
    dy = (int8_t)data->new[2];

    if (button & 0x01)
        printf("Left button pressed\n");
    if (button & 0x02)
        printf("Right button pressed\n");
    if (button & 0x04)
        printf("Middle button pressed\n");

    if (dx || dy)
        printf("Mouse moved: dx=%d, dy=%d\n", dx, dy);

    memcpy(data->old, data->new, USB_MOUSE_PACKET_SIZE);

    return 1;
}

/* Mouse interrupt handler */
static int usb_mouse_irq(struct usb_device *dev)
{
    if ((dev->irq_status != 0) ||
        (dev->irq_act_len != USB_MOUSE_PACKET_SIZE)) {
        printf("USB MOUSE: Error %lX, len %d\n",
              dev->irq_status, dev->irq_act_len);
        return 1;
    }

    return usb_mouse_irq_worker(dev);
}

/* Interrupt polling */
 void usb_mouse_poll_for_event(struct usb_device *dev)
{    struct usb_mouse_pdata *data = dev->privptr;
     if (poll_int_queue(dev, data->intq)) {
        usb_mouse_irq_worker(dev);
        /* We've consumed all queued int packets, create new */
        destroy_int_queue(dev, data->intq);
        data->intq = create_int_queue(dev, data->intpipe, 1,
                                  USB_MOUSE_PACKET_SIZE, data->new,
                                  data->intinterval);
     }
     else
      return 0;
}
// #if defined(CONFIG_SYS_USB_EVENT_POLL)
//     // 通过控制端点或中断队列等方式处理USB鼠标事件
//     // usb_submit_control_msg() 或 poll_int_queue() 等函数调用
//     // 在此处添加相应的代码
// #elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP)
//     struct usb_interface *iface;
//     struct usb_mouse_pdata *data = dev->privptr;
//     iface = &dev->config.if_desc[0];
//     usb_get_report(dev, iface->desc.bInterfaceNumber,
//                    1, 0, data->new, USB_MOUSE_REPORT_SIZE);
//     if (memcmp(data->old, data->new, USB_MOUSE_REPORT_SIZE)) {
//         usb_mouse_irq_worker(dev);
// #else
//     //printf("mouse_poll\n");
//     struct usb_mouse_pdata *data = dev->privptr;
//     if (poll_int_queue(dev, data->intq)) {
//         usb_mouse_irq_worker(dev);
//         /* We've consumed all queued int packets, create new */
//         destroy_int_queue(dev, data->intq);
//         data->intq = create_int_queue(dev, data->intpipe, 1,
//                                   USB_MOUSE_PACKET_SIZE, data->new,
//                                   data->intinterval);
// #endif
//         data->last_report = get_timer(0);
//     }else if (data->last_report != -1 &&
//                get_timer(data->last_report) > REPEAT_RATE) {
//         usb_mouse_irq_worker(dev);
//         data->last_report = get_timer(0);
//     }
// }

/* probes the USB device dev for mouse type. */
static int usb_mouse_probe_dev(struct usb_device *dev, unsigned int ifnum)
{
    struct usb_interface *iface;
    struct usb_endpoint_descriptor *ep;
    struct usb_mouse_pdata *data;

    if (dev->descriptor.bNumConfigurations != 1)
        return 0;

    iface = &dev->config.if_desc[ifnum];

    if (iface->desc.bInterfaceClass != USB_CLASS_HID)
        return 0;

    if (iface->desc.bInterfaceSubClass != USB_SUB_HID_BOOT)
        return 0;

    if (iface->desc.bInterfaceProtocol != USB_PROT_HID_MOUSE)
        return 0;

    if (iface->desc.bNumEndpoints != 1)
        return 0;

    ep = &iface->ep_desc[0];

    /* Check if endpoint 1 is interrupt endpoint */
    if (!(ep->bEndpointAddress & 0x80))
        return 0;

    if ((ep->bmAttributes & 3) != 3)
        return 0;

    printf("USB MOUSE: found set protocol...\n");

    data = malloc(sizeof(struct usb_mouse_pdata));
    if (!data) {
        printf("USB MOUSE: Error allocating private data\n");
        return 0;
    }

    /* Clear private data */
    memset(data, 0, sizeof(struct usb_mouse_pdata));

    /* allocate input buffer aligned and sized to USB DMA alignment */
    data->new = memalign(USB_DMA_MINALIGN,
                         roundup(USB_MOUSE_PACKET_SIZE, USB_DMA_MINALIGN));

    /* Insert private data into USB device structure */
    dev->privptr = data;

    /* Set IRQ handler */
    dev->irq_handle = usb_mouse_irq;

    data->intpipe = usb_rcvintpipe(dev, ep->bEndpointAddress);
    data->intpktsize = min(usb_maxpacket(dev, data->intpipe),
                           USB_MOUSE_PACKET_SIZE);
    data->intinterval = ep->bInterval;
    data->last_report = -1;
    printf("**mouse intpipe=%lu,intpktsize=%d,intinterval=%d\n",data->intpipe,data->intpktsize,data->intinterval = ep->bInterval);
    /* We found a USB Mouse, install it. */
    usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0);

    printf("USB MOUSE: found set idle...\n");
    usb_set_idle(dev, iface->desc.bInterfaceNumber, 0, 0);

    printf("USB MOUSE: enable interrupt pipe...\n");
    data->intq = create_int_queue(dev, data->intpipe, 1,
				      3, data->new,
				      data->intinterval);
	// if (usb_submit_int_msg(dev, data->intpipe, data->new, data->intpktsize,
    //                        data->intinterval) < 0) {
    //     printf("Failed to get mouse state from device %04x:%04x\n",
    //            dev->descriptor.idVendor, dev->descriptor.idProduct);
    //     /* Abort, we don't want to use that non-functional mouse. */
    //     return 0;
    // }

    /* Success. */
    printf("mouse intq=%p\n",data->intq);
    return 1;
}

static int probe_usb_mouse(struct usb_device *dev)
{
    char *stdinname;
    struct stdio_dev usb_mouse_dev;
    int error;

    /* Try probing the mouse */
    if (usb_mouse_probe_dev(dev, 0) != 1)
        return -ENOENT;

    /* Register the mouse */
    printf("USB MOUSE: register.\n");
    memset(&usb_mouse_dev, 0, sizeof(struct stdio_dev));
    strcpy(usb_mouse_dev.name, DEVNAME);
    usb_mouse_dev.flags =  DEV_FLAGS_INPUT;
    usb_mouse_dev.getc = NULL;
    usb_mouse_dev.tstc = NULL;
    usb_mouse_dev.priv = (void *)dev;
    error = stdio_register(&usb_mouse_dev);
    if (error)
        return error;
    stdinname = env_get("stdin");
    printf("stdinname=%s\n", stdinname);
#if CONFIG_IS_ENABLED(CONSOLE_MUX)
    error = iomux_doenv(stdin, stdinname);
    if (error) {
        printf("error=%d\n", error);
        return error;
    }
#else
    /* Check if this is the standard input device. */
    // if (strcmp(stdinname, "USBMOUSE") != 0) {
    //     return 1;
    // }

    /* Reassign the console */
    if (overwrite_console()) {
        return 1;
    }

    // error = console_assign(stdin, DEVNAME);
    // printf("error=%d\n",error);
    // if (error)
    //     return error;
#endif

    return 0;
}

#ifndef CONFIG_DM_USB
/* Search for mouse and register it if found. */
int drv_usb_mouse_init(void)
{
    int error, i;

    debug("%s: Probing for mouse\n", __func__);
    /* Scan all USB Devices */
    for (i = 0; i < USB_MAX_DEVICE; i++) {
        struct usb_device *dev;

        /* Get USB device. */
        dev = usb_get_dev_index(i);
        if (!dev)
            break;

        if (dev->devnum == -1)
            continue;

        error = probe_usb_mouse(dev);
        if (!error)
            return 1;
        if (error && error != -ENOENT)
            return error;
    }

    /* No USB Mouse found */
    return -1;
}

/* Deregister the mouse. */
int usb_mouse_deregister(int force)
{
#if CONFIG_IS_ENABLED(SYS_STDIO_DEREGISTER)
    struct stdio_dev *dev;
    struct usb_device *usb_mouse_dev;
    struct usb_mouse_pdata *data;

    dev = stdio_get_by_name(DEVNAME);
    if (dev) {
        usb_mouse_dev = (struct usb_device *)dev->priv;
        data = usb_mouse_dev->privptr;
        if (stdio_deregister_dev(dev, force) != 0)
            return 1;
#if CONFIG_IS_ENABLED(CONSOLE_MUX)
        if (iomux_doenv(stdin, env_get("stdin")) != 0)
            return 1;
#endif
        free(data->new);
        free(data);
    }

    return 0;
#else
    return 1;
#endif
}

#endif

#ifdef CONFIG_DM_USB

static int usb_mouse_probe(struct udevice *dev)
{
    struct usb_device *udev = dev_get_parent_priv(dev);

    return probe_usb_mouse(udev);
}

static int usb_mouse_remove(struct udevice *dev)
{
    struct usb_device *udev = dev_get_parent_priv(dev);
    struct usb_mouse_pdata *data;
    struct stdio_dev *sdev;
    int ret;

    sdev = stdio_get_by_name(DEVNAME);
    if (!sdev) {
        ret = -ENXIO;
        goto err;
    }
    data = udev->privptr;
    if (stdio_deregister_dev(sdev, true)) {
        ret = -EPERM;
        goto err;
    }
#if CONFIG_IS_ENABLED(CONSOLE_MUX)
    if (iomux_doenv(stdin, env_get("stdin"))) {
        ret = -ENOLINK;
        goto err;
    }
#endif
    free(data->new);
    free(data);

    return 0;
err:
    printf("%s: warning, ret=%d", __func__, ret);
    return ret;
}

static const struct udevice_id usb_mouse_ids[] = {
	{ .compatible = "usb-mouse" },
	{ }
};

U_BOOT_DRIVER(usb_mouse) = {
	.name	= "usb_mouse",
	.id	= UCLASS_MOUSE,
	.of_match = usb_mouse_ids,
	.probe = usb_mouse_probe,
	.remove = usb_mouse_remove,
};

UCLASS_DRIVER(usb_mouse) = {
	.id		= UCLASS_MOUSE,
	.name		= "usb_mouse",
};

static const struct usb_device_id mouse_id_table[] = {
	{
		.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
			USB_DEVICE_ID_MATCH_INT_SUBCLASS |
			USB_DEVICE_ID_MATCH_INT_PROTOCOL,
		.bInterfaceClass = USB_CLASS_HID,
		.bInterfaceSubClass = USB_SUB_HID_BOOT,
		.bInterfaceProtocol = USB_PROT_HID_MOUSE,
	},
	{ }
};

U_BOOT_USB_DEVICE(usb_mouse, mouse_id_table);

#endif
